@tool
@icon("icons/fsm.svg")
class_name FSM 
extends Node

@export_node_path("State") var init_state_path:NodePath
@export var expression_properties:Dictionary = {}
var cur_state_name:String:
	get:
		return "" if not cur_state else cur_state.state_name
var cur_state:State = null
var sub_states:Array[State] = []
var state_dict:Dictionary

var cur_owner
var enable:bool = false
# 转换的最终目标State
var target_state:State
# 上一个转换的最终目标State
var last_target_state:State

func init_fsm(cur_owner):
	self.cur_owner = cur_owner
	state_dict = {}
	for state:State in get_all_states():
		sub_states.append(state)
		state_dict[state.state_name] = state
		state.state_init(self)
		state.on_translate_state.connect(translate_state)
	enable = true
	var init_state = get_node_or_null(init_state_path)
	translate_state(null,init_state)

func get_cur_state() -> State:
	return cur_state
func get_ready_states(to_state_name:String,index:int = 1) -> Array:
	var ready_state_array:Array = []
	if state_dict.has(to_state_name) == false or (index != -1 && index >= state_dict[to_state_name].size()):
		return ready_state_array
	if index == -1:
		var state_array:Array = state_dict[to_state_name]
		ready_state_array = state_array.filter(func (state:State): return state.get_state_status() == GameEnum.StateStatus.Ready)
	else:
		var state:State = state_dict[to_state_name][index]
		if state.get_state_status() == GameEnum.StateStatus.Ready:
			ready_state_array.append(state)
	return ready_state_array
func translate_state_by_state_name(state_name):
	if state_dict.has(state_name):
		translate_state(cur_state,state_dict[state_name])
func translate_state(st_state:State,to_state:State):
	last_target_state = st_state
	target_state = to_state
	var st_state_path:Array[State] = get_state_path(st_state)
	var to_state_path:Array[State] = get_state_path(to_state)
	var st_state_temp:State = st_state_path.pop_back()
	var to_state_temp:State = to_state_path.pop_back()
	if not st_state_temp and to_state_temp:
		cur_state = to_state_temp
		cur_state.translate_state(st_state,to_state,st_state_path,to_state_path)
	elif st_state_temp.state_name == to_state_temp.state_name:
		to_state_temp.translate_state(st_state,to_state,st_state_path,to_state_path)
	else:
		cur_state.on_exit(to_state)
		cur_state = to_state_temp
		cur_state.translate_state(st_state,to_state,st_state_path,to_state_path)
func get_state_path(state):
	var path:Array[State] = []
	while state:
		if state is State:
			path.append(state)
			state = state.get_parent()
		else:
			state = null
	return path
func set_expression_property(name:StringName,value) -> void:
	if not is_node_ready():
		return
	if expression_properties.has(name) and typeof(expression_properties[name]) != typeof(value):
		return
	expression_properties[name] = value
func get_all_states() -> Array[State]:
	var states:Array[State] = []
	var state_queue:Array[State] = []
	for child in get_children():
		if child is State:
			states.append(child)
			state_queue.append(child)
	while state_queue.size() > 0:
		var temp_state = state_queue.pop_front()
		for child in temp_state.get_children():
			if child is State:
				states.append(child)
				state_queue.append(child)
	return states
func _get_configuration_warnings() -> PackedStringArray:
	var result = []
	var found = false
	var child_cound:int = 0
	for child in get_children():
		if child is State:
			child_cound += 1
	if child_cound < 1:
		result.append("FSM至少有一个State作为初始State")
	var temp_init_state = get_node_or_null(init_state_path)
	if not temp_init_state:
		result.append("初始State找不到")
	elif temp_init_state.get_parent() != self:
		result.append("初始State必须是FSM的直接子节点")
	return result

func _process(delta: float) -> void:
	if cur_state and enable and get_parent().is_node_ready():
		cur_state.on_update(delta)
func _physics_process(delta: float) -> void:
	if cur_state and enable and get_parent().is_node_ready():
		cur_state.on_physics_update(delta)
	
